package phoenixAAD.WifiFlashDrive;


import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Scanner;

import org.apache.http.util.EncodingUtils;


public class SendData extends Transfer
{
	private static final int BUFFER_SIZE = 1024;
	private final DataType dataType;
	private final TransferMode mode;
	private final DataStructure structure;
	private final Socket socket;
	private final Protocol toProtocol;
	private final File file;
	
	
	private static final String REPLACE = "\\p{Cntrl}", REPLACE_WITH = "";
	
	private long filesize;
	
	private String sendString;
	private DataOutputStream out;
	private InputStream input;
	private final FTP ftp;
	private String encoding = "UTF-8";
	
	
	
	/**
	 * Sends the directory listing to the client
	 * @param socket - the socket that is connected with client
	 * @param directory - the file object of the folder
	 */
	public SendData(Protocol toProtocol, Socket socket, File folder, FTP ftp, String encoding)
	{
		this.ftp = ftp;
		this.encoding = encoding;
		this.toProtocol = toProtocol;
		this.socket = socket;
		mode = null;
		structure = null;
		file = null;
		StringBuffer toSend = new StringBuffer();
		
		// convert to UNIX format
		// NOTE: command "ls -l" prints time also, breaking windows native FTP
		SimpleDateFormat dateConverter = new SimpleDateFormat("MMM dd yyyy", Locale.US);
		for(File file : folder.listFiles())
		{
			// permissions
			toSend.append(file.isDirectory() ? 'd' : '-');
			for(int i = 0; i < 3; i++)
			{
				toSend.append(file.canRead() ? 'r' : '-');
				toSend.append(file.canWrite() ? 'w' : '-');
				// no method to check for can execute
				toSend.append('-');
			}
			toSend.append(' ');
			// user & group
			toSend.append("ftp");
			toSend.append(' ');
			toSend.append("ftp");
			toSend.append(' ');
			
			// file size, undefined for directory
			if(file.isFile())
				toSend.append(file.length());
			else
				toSend.append('0');
			toSend.append(' ');
			
			// file modified
			Date fileModified = new Date(file.lastModified());
			toSend.append(dateConverter.format(fileModified));
			toSend.append(' ');
					
			// file name
			toSend.append(file.getName());
			
			toSend.append(Response.EOL);
		}
		sendString = toSend.toString();
		dataType= DataType.LIST;			
	}

	/**
	 * Sends the specified file's content to the client
	 * @param socket - the socket that is connected with client
	 * @param file - the file to send to the client
	 * @param dataType - the type of data that should be sent, ASCII or IMAGE
	 * @param mode - the transfer mode to use
	 * @param structure - the file structure the server should send in
	 */
	public SendData(Protocol toProtocol, Socket socket, File file, DataType dataType, TransferMode mode, DataStructure structure, FTP ftp)
	{
		this.ftp = ftp;
		this.toProtocol = toProtocol;
		this.socket = socket;
		
		this.dataType = dataType;
		this.mode = mode;
		this.structure = structure;		
		this.file = file;
		
		filesize = file.length();
	}

	@Override
	public void run() {
		try {
			out = new DataOutputStream(socket.getOutputStream());
			switch(dataType)
			{
			case LIST:
			{
				byte[] stringBytes = sendString.getBytes(encoding);//EncodingUtils.getAsciiBytes(sendString);	
				input = new ByteArrayInputStream(stringBytes);
				sendAsStream();
				input.close();
				break;
			}
			case ASCII_NON_PRINT:
			{
				StringBuilder builder = new StringBuilder();
				Scanner scanner = new Scanner(file);
				while(scanner.hasNextLine())
				{
					builder.append(scanner.nextLine().replace(REPLACE, REPLACE_WITH));
				}
				input = new ByteArrayInputStream(EncodingUtils.getAsciiBytes(builder.toString()));
				if(mode == TransferMode.STREAM)
					sendAsStream();
				else if(mode == TransferMode.BLOCK)
					sendAsBlock();
				input.close();
				break;
			}
			case IMAGE:
			{
				input = new FileInputStream(file);
				if(mode == TransferMode.STREAM)
					sendAsStream();
				else if(mode == TransferMode.BLOCK)
					sendAsBlock();
				input.close();
				break;
			}
			}
			
			
			out.close();
			socket.close();
			toProtocol.transferFinished(Protocol.TRANSFER_FINISHED, this);
		} catch (IOException e)
		{
			if(out != null)
				try {
					out.close();
				} catch (IOException e1) {}
			try {
				socket.close();
			} catch (IOException e1) {}
			toProtocol.transferFinished(Protocol.EXCEPTION, this);
		}
	}
	
	
	
	/**
	 * Sends the InputStream to the DataOutputStream using stream mode
	 * @throws IOException
	 */
	private void sendAsStream() throws IOException
	{		
		byte[] buffer = new byte[BUFFER_SIZE];
		int readBytes = 0;
		int bytePos = 0 ;
		do
		{
			out.write(buffer, 0, readBytes);
			readBytes = input.read(buffer);
			if(dataType.equals(DataType.ASCII_NON_PRINT)|| dataType.equals(DataType.IMAGE)){
				bytePos+=readBytes;
				ftp.sendProgressStatus((int) ((float)bytePos/(float)filesize*100));
			}
		} while(readBytes != -1);
		
		out.flush();
	}
	
	
	//private static final byte RESTART_MARKER = 16;
	//private static final byte EOF_MARKER = 64;
	@Deprecated
	private void sendAsBlock() throws IOException
	{
		/*
		long currentPosition = 0;
		byte[] buffer = new byte[BUFFER_SIZE];
		int readBytes = 0;
		
		readBytes = input.read(buffer);
		if(readBytes != -1)
		{
			currentPosition += readBytes;
			// Header
			
			
			// Restart Marker
			out.write(RESTART_MARKER);
			out.write(8); // size of long
			out.writeLong(currentPosition);
			
		}
		out.write(buffer, 0, readBytes);
			
			*/
		
	}
	
	public void close()
	{
		try {
			out.close();
		} catch (IOException e) {
		}
	}
}
